home *** CD-ROM | disk | FTP | other *** search
- /*
-
- apply.c
-
- This software is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This software is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this software; if not, write to the Free
- Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Original copyright notice follows:
-
- Copyright, 1993, Brent Benson. All Rights Reserved.
- 0.4 & 0.5 Revisions Copyright 1994, Joseph N. Wilson. All Rights Reserved.
-
- Permission to use, copy, and modify this software and its
- documentation is hereby granted only under the following terms and
- conditions. Both the above copyright notice and this permission
- notice must appear in all copies of the software, derivative works
- or modified version, and both notices must appear in supporting
- documentation. Users of this software agree to the terms and
- conditions set forth in this notice.
-
- */
-
- #include "apply.h"
-
- #include "alloc.h"
- #include "class.h"
- #include "env.h"
- #include "eval.h"
- #include "error.h"
- #include "function.h"
- #include "keyword.h"
- #include "list.h"
- #include "print.h"
- #include "prim.h"
- #include "symbol.h"
- #include "syntax.h"
- #include "values.h"
-
- /* global data */
- int trace_functions = 0;
- int trace_only_user_funs = 0;
- int trace_level = 0;
- Object ResultValueStack;
-
- #ifdef MACOS
- void check_stack (void);
-
- #endif
-
- /* local function prototypes and data */
-
- Object apply_generic (Object gen, Object args);
- static void narrow_value_types (Object *values_list,
- Object new_values_list,
- Object *rest_type,
- Object new_rest_type);
- static Object apply_exit (Object exit_proc, Object args);
- static Object apply_next_method (Object next_method, Object args);
- static Object set_trace (Object bool);
- static void devalue_args (Object args);
- static Object user_keyword;
-
- /* primitives */
-
- static struct primitive apply_prims[] =
- {
- {"%apply", prim_2, apply},
- {"%trace", prim_1, set_trace},
- {"%eval", prim_1, eval},
- };
-
- /* function definitions */
-
- void
- init_apply_prims (void)
- {
- int num;
-
- num = sizeof (apply_prims) / sizeof (struct primitive);
-
- init_prims (num, apply_prims);
-
- user_keyword = make_keyword ("user:");
- ResultValueStack = make_empty_list ();
- }
-
- Object
- default_result_value (void)
- {
- return cons (make_empty_list (), object_class);
- }
-
- Object
- apply_internal (Object fun, Object args)
- {
- Object ret;
-
- #ifdef MACOS
- check_stack ();
- #endif
-
- if (trace_functions) {
- int i;
-
- if ((!trace_only_user_funs) || (!PRIMP (fun))) {
- printf ("; ");
- for (i = 0; i < trace_level; ++i) {
- putchar ('-');
- }
- print_object (stdout, fun, 1);
- printf (" called with ");
- print_object (stdout, args, 1);
- printf ("\n");
- trace_level++;
- }
- }
- #ifdef SMALL_OBJECTS
- if (!POINTERP (fun)) {
- return error ("apply: cannot apply this object", fun, NULL);
- }
- #endif
-
- devalue_args (args);
- switch (POINTERTYPE (fun)) {
- case Primitive:
- ret = apply_prim (fun, args);
- break;
- case Method:
- ret = apply_method (fun, args, make_empty_list (), 0);
- break;
- case GenericFunction:
- ret = apply_generic (fun, args);
- break;
- case NextMethod:
- ret = apply_next_method (fun, args);
- break;
- case Exit:
- ret = apply_exit (fun, args);
- break;
- default:
- error ("apply: cannot apply this object", fun, NULL);
- }
- if (trace_functions && trace_level) {
- int i;
-
- if ((!trace_only_user_funs) || (!PRIMP (fun))) {
- trace_level--;
- printf ("; ");
- for (i = 0; i < trace_level; ++i) {
- printf ("-");
- }
- printf ("returned: ");
- print_object (stdout, ret, 1);
- printf ("\n");
- }
- }
- return (ret);
- }
-
- /* local functions */
-
- /*
- * It seems to me that apply method has gotten a little big.
- * It could benefit from modularizing in a rewrite.
- * -jnw
- */
- Object
- apply_method (Object meth, Object args, Object rest_methods, int generic_apply)
- {
- Object params, param, sym, val, body, ret;
- Object ret_types, tmp, dup_list;
- Object rest_var, class, keyword, keys, key_decl;
- Object *tmp_ptr, old;
- int hit_rest, hit_key, hit_values;
- struct frame *old_env;
- int i, j;
-
-
- if (trace_functions && trace_level) {
- int i;
-
- if (!trace_only_user_funs) {
- printf ("; ");
- for (i = 0; i < trace_level; ++i) {
- putchar ('-');
- }
- printf ("apply-method applying ");
- print_object (stdout, meth, 1);
- printf (" with args ");
- print_object (stdout, args, 1);
- printf ("\n");
- }
- }
- ret = unspecified_object;
- params = METHREQPARAMS (meth);
- body = METHBODY (meth);
-
-
- /* remember current environment and subsitute with
- environment present at method creation time */
- old_env = the_env;
- the_env = METHENV (meth);
-
- push_scope (meth);
-
- /* install of next method object if there are next methods */
- if (PAIRP (rest_methods)) { /* check use of empty_list vs. NULL!! */
- Object next_method;
-
- next_method = make_next_method (rest_methods, args);
- push_scope (next_method);
- add_binding (METHNEXTMETH (meth), next_method, 0);
- }
- hit_rest = hit_key = hit_values = 0;
-
- /* first process required parameters */
- while ((PAIRP (params) && PAIRP (args))
- && (!hit_rest) && (!hit_key) && !(hit_values)) {
- param = CAR (params);
- if (param == hash_rest_symbol) {
- hit_rest = 1;
- } else if (param == key_symbol) {
- hit_key = 1;
- } else if (param == hash_values_symbol) {
- hit_values = 1;
- } else {
- val = CAR (args);
- if (SYMBOLP (param)) {
- sym = param;
- } else {
- sym = FIRST (param);
- class = SECOND (param);
- if (!instance (val, class)) {
- error ("apply: argument doesn't match method specializer",
- val, class, meth, NULL);
- }
- }
- add_binding (sym, val, 0);
- args = CDR (args);
- params = CDR (params);
- }
- }
- /* now process #rest and #key parameters */
-
- if ((rest_var = METHRESTPARAM (meth)) != NULL) {
- add_binding (rest_var, args, 0);
- }
- if (PAIRP (METHKEYPARAMS (meth))) {
- /* copy keys */
- keys = copy_list (METHKEYPARAMS (meth));
-
- dup_list = make_empty_list (); /* For duplicate keywords */
-
- /* Bind each of the keyword args that is present. */
- while (!NULLP (args)) {
- keyword = FIRST (args);
- if (!KEYWORDP (keyword)) {
- /* jnw -- check this out! */
- if (!rest_var) {
- error ("apply: argument to method must be keyword", meth, keyword, NULL);
- } else {
- args = CDR (args);
- continue;
- }
- }
- val = SECOND (args);
-
- /* if keyword is in the keys list then
- * 1) add a binding for keyword to val
- * 2) remove the keyword entry from keys
- */
-
- for (tmp_ptr = &keys;
- PAIRP (*tmp_ptr);
- tmp_ptr = &CDR (*tmp_ptr)) {
- if (CAR (CAR (*tmp_ptr)) == keyword) {
- break;
- }
- }
- if (EMPTYLISTP (*tmp_ptr)) {
- if (member (keyword, dup_list)) {
- warning ("Duplicate keyword value ignored",
- keyword, val, NULL);
- } else if (!METHALLKEYS (meth) && !generic_apply) {
- error ("Keyword argument not in parameter list or given twice",
- keyword, NULL);
- }
- } else {
- add_binding (SECOND (CAR (*tmp_ptr)), val, 0);
- dup_list = cons (keyword, dup_list);
- *tmp_ptr = CDR (*tmp_ptr);
- }
- args = CDR (CDR (args));
- }
- /* Bind the missing keyword args to default_object */
- while (PAIRP (keys)) {
- add_binding (SECOND (CAR (keys)), eval (THIRD (CAR (keys))), 0);
- keys = CDR (keys);
- }
-
- }
- if (PAIRP (args) && !rest_var) {
- /*
- * Shouldn't check for all args used if applying method through
- * a generic function or as a next method.
- * Must check if applying directly.
- */
- if (METHALLKEYS (meth)) {
- /* skip rest of parameters if they are keywords */
- while (PAIRP (args)) {
- if (!KEYWORDP (CAR (args))) {
- error ("apply: keyword argument expected", CAR (args),
- NULL);
- } else if (!PAIRP (CDR (args))) {
- error ("apply: keyword has no associated argument value",
- CAR (args), NULL);
- }
- args = CDR (CDR (args));
- }
- } else {
- error ("Arguments have no matching parameters", args, NULL);
- }
- }
- if (PAIRP (params)) {
- error ("Required parameters have no matching arguments", params,
- NULL);
- }
- while (!NULLP (body)) {
- Object form = CAR (body);
-
- #ifdef OPTIMIZE_TAIL_CALLS
- /* when in tail form, we use tail_eval */
- if (NULLP (CDR (body))) {
- if (trace_functions) {
- if (!trace_only_user_funs)
- warning ("tail position: ", form, NULL);
- if (trace_level)
- --trace_level;
- }
- /* tail recursion optimization. */
-
- /* If return values of this method are narrower types
- * than what is currently on top of the ResultValueStack,
- * trim it down to match.
- */
-
-
- narrow_value_types (&CAR (CAR (ResultValueStack)),
- METHREQVALUES (meth),
- &CDR (CAR (ResultValueStack)),
- METHRESTVALUES (meth));
-
- ret = tail_eval (form);
- } else {
- #endif
-
- ret = construct_return_values (eval (form),
- METHREQVALUES (meth),
- METHRESTVALUES (meth));
- #ifdef OPTIMIZE_TAIL_CALLS
- }
- #endif
-
- body = CDR (body);
- }
- pop_scope (); /* When the_env disappears, we'll need this pop_scope()! */
-
- /* re-assert environment present at the beginning of this function
- */
- the_env = old_env;
-
- return ret;
- }
-
- static void
- narrow_value_types (Object *values_list_ptr,
- Object new_values_list,
- Object *rest_type,
- Object new_rest_type)
- {
- Object values_list;
-
- /* First check each value common to both lists.
- * If a new value is a subtype, substitute it.
- */
- for (; !EMPTYLISTP (*values_list_ptr);
- values_list_ptr = &CDR (*values_list_ptr),
- new_values_list = CDR (new_values_list)) {
- if (EMPTYLISTP (new_values_list)) {
- break;
- }
- if (subtype (CAR (new_values_list), CAR (*values_list_ptr))) {
- CAR (*values_list_ptr) = CAR (new_values_list);
- }
- }
-
- if (EMPTYLISTP (*values_list_ptr)) {
- /* We had enough values in the new list to match all the old ones */
-
- /* If there were more new_values than old.
- * They must match the rest type of the old list, and must
- * be added to the list.
- */
- while (!EMPTYLISTP (new_values_list)) {
- if (subtype (CAR (new_values_list), *rest_type)) {
- *values_list_ptr = cons (CAR (new_values_list),
- make_empty_list ());
- } else {
- *values_list_ptr = cons (*rest_type, make_empty_list ());
- }
- values_list_ptr = &CDR (*values_list_ptr);
- new_values_list = CDR (new_values_list);
- }
- } else {
- /* We didn't match all the values.
- * Make sure the remaining values are equally as narrow as
- * new_rest_values
- */
- if (new_rest_type == NULL) {
- error ("Incompatible value specification in call", NULL);
- }
- values_list = *values_list_ptr;
- while (!EMPTYLISTP (values_list)) {
- if (subtype (new_rest_type, CAR (values_list))) {
- CAR (values_list) = new_rest_type;
- }
- values_list = CDR (values_list);
- }
- }
- if (new_rest_type == NULL) {
- /* No rest values are allowed to be returned */
- *rest_type = NULL;
- } else if (*rest_type == NULL || subtype (*rest_type, new_rest_type)) {
- *rest_type = new_rest_type;
- }
- }
-
- Object
- construct_return_values (Object ret,
- Object required_values,
- Object rest_values)
- {
- int i, j;
- Object newret;
-
- /* To save effort, I make sure the return is a VALUES object.
- * This is a waste of effort and really ought to be fixed.
- * <pcb> could at least wrap it in a stack variable to avoid an alloc.
- */
-
- ResultValueStack = cons (default_result_value (), ResultValueStack);
-
- if (!VALUESP (ret)) {
- ret = make_values (listem (ret, NULL));
- }
- /* check return values (not done for non VALUESTYPE values yet */
- for (i = 0;
- i < VALUESNUM (ret) && PAIRP (required_values);
- i++, required_values = CDR (required_values)) {
- if (!instance (VALUESELS (ret)[i], CAR (required_values))) {
- error ("in value return: return value is not of correct type",
- VALUESELS (ret)[i], CAR (required_values), NULL);
- }
- }
- if (i < VALUESNUM (ret)) {
- /* We have more return values than specific return types.
- * Check them against the #rest value return type
- */
- if (rest_values != NULL) {
- for (; i < VALUESNUM (ret); i++) {
- if (!instance (VALUESELS (ret)[i],
- rest_values)) {
- error ("in value return: return value is not of correct type",
- VALUESELS (ret)[i],
- rest_values,
- NULL);
- }
- }
- } else {
- /* Discard the extra values by ignoring them. */
- VALUESNUM (ret) = i;
- }
- } else if (PAIRP (required_values)) {
- /* Add default values */
- for (j = 0; PAIRP (required_values); j++, required_values = CDR (required_values)) {
- if (!instance (false_object, CAR (required_values))) {
- error ("in value return: default value doesn't match return type",
- CAR (required_values),
- NULL);
- }
- }
- newret = allocate_object (sizeof (struct values));
-
- VALUESTYPE (newret) = Values;
- VALUESNUM (newret) = i + j;
- VALUESELS (newret) = (Object *)
- checking_malloc (VALUESNUM (newret) * sizeof (Object));
-
- for (i = 0; i < VALUESNUM (ret); i++) {
- VALUESELS (newret)[i] = VALUESELS (ret)[i];
- }
- for (; i < VALUESNUM (newret); i++) {
- VALUESELS (newret)[i] = false_object;
- }
- ret = newret;
- }
- /* turn stupid multiple value into single value */
- if (VALUESNUM (ret) == 1) {
- ret = VALUESELS (ret)[0];
- }
- ResultValueStack = CDR (ResultValueStack);
- return (ret);
- }
-
- Object
- apply_generic (Object gen, Object args)
- {
- Object methods, sorted_methods;
-
- methods = GFMETHODS (gen);
- sorted_methods = FIRSTVAL (sorted_applicable_methods (gen, args));
- if (EMPTYLISTP (sorted_methods)) {
- error ("Ambiguous methods in apply generic function", gen, args, NULL);
- } else {
- return apply_method (CAR (sorted_methods),
- args,
- CDR (sorted_methods),
- 1);
- }
- }
-
- static Object
- apply_exit (Object exit_proc, Object args)
- {
- Object vals;
-
- unwind_to_exit (EXITSYM (exit_proc));
- switch (list_length (args)) {
- case 0:
- longjmp (*EXITRET (exit_proc), (int) (unspecified_object));
- case 1:
- longjmp (*EXITRET (exit_proc), (int) FIRST (args));
- default:
- longjmp (*EXITRET (exit_proc), (int) (values (args)));
- }
- }
-
- static Object
- apply_next_method (Object next_method, Object args)
- {
- Object rest_methods, real_args;
-
- rest_methods = NMREST (next_method);
- if (NULLP (args)) {
- real_args = NMARGS (next_method);
- } else {
- real_args = args;
- }
- return apply_method (CAR (rest_methods), real_args, CDR (rest_methods), 1);
- }
-
- static Object
- set_trace (Object flag)
- {
- if (flag == false_object) {
- trace_functions = 0;
- trace_only_user_funs = 0;
- } else {
- trace_functions = 1;
- if (flag == user_keyword) {
- trace_only_user_funs = 1;
- }
- }
- return (flag);
- }
-
- static void
- devalue_args (Object args)
- {
- while (!EMPTYLISTP (args)) {
- Object arg = CAR (args);
-
- if (VALUESP (arg)) {
- if (VALUESNUM (arg) > 0) {
- CAR (args) = VALUESELS (arg)[0];
- } else {
- error ("Null values construct used as an argument", NULL);
- }
- }
- args = CDR (args);
- }
- }
-